// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef _COMMON_MASTER_H_
#define _COMMON_MASTER_H_

#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <thread>
#include <mutex>
#include <map>
#include <vector>
#include <unordered_map>

#include "RedisHandler.h"
#include "SocketServer.h"

namespace master {

const std::string DEFAULT_DELIMITER = ":";
const std::string DEFAULT_SPACE = " ";
const int DEFAULT_ASSIGN_TIME = 4000000;
const int DEFAULT_BUF_SIZE = 1024;
const int DEFAULT_HEARTBEAT_CHECK_INTERNAL = 2; // 单位秒
const int INIT_REMOTE_SCHEDULING_REQ_SIZE = 36;

typedef struct WorkContextMgS {
    std::string ip;
    uint32_t port;
    uint32_t status;
    uint8_t encodeType;
    uint8_t resourceType;
    uint8_t dataType;
    uint8_t flavorType;
    uint8_t frameRate;
    int fd;
    uint64_t heartbeatTime;
    uint64_t assignTime;
    std::string phoneId;
    WorkContextMgS &operator=(WorkContextMgS &src)
    {
        ip = src.ip;
        port = src.port;
        status = src.status;
        encodeType = src.encodeType;
        resourceType = src.resourceType;
        dataType = src.dataType;
        flavorType = src.flavorType;
        frameRate = src.frameRate;
        fd = src.fd;
        heartbeatTime = src.heartbeatTime;
        assignTime = src.assignTime;
        phoneId = src.phoneId;
        return *this;
    }
} WorkContextMgT;

class CommonMaster {
public:
    static CommonMaster& GetInstance()
    {
        static CommonMaster gMaster;
        return gMaster;
    }
    CommonMaster();
    ~CommonMaster();

    int serverParseData(std::shared_ptr<RedisOperator> &redisClient, uint16_t cmdCtl, uint8_t *pOutData, uint32_t outLen, int fd, std::string requestId);

    int commonMasterReadDataAnalysis(int fd, std::string requestId);

    int commonMasterWriteData(uint16_t cmdCtl, uint8_t *inBuf, uint32_t inLen, int fd, std::string requestId);

    int masterServiceInit(uint16_t ipPort);

    int dealConfig(const std::string configFilePath);

    int masterServiceClose(int fd);

    uint32_t getIdleWorkerCount();

    int getPrivateIp(const std::string &netName, std::string &privateIp);

private:
    int createThreadInit(void);

    int findUuidSocketFdInfoIt(std::string workerUuid);

    void insertUuidSocketFdInfoIt(std::string workerUuid, int fd);

    void eraseUuidSocketFdInfoIt(std::string workerUuid);

    static void *ServiceThreadInit(void *pArgc);

    int ServiceThreadFunc(void);

    static void *ServiceHeartBeatThreadInit(void *pArgc);

    void ServiceWorkerHeartBeatFunc(void);

    int eraseHeartbeatTimeOutUuid();

    void statisticWorkerStatus(uint32_t workerStatus, uint32_t *idleCount, uint32_t *assignCount, uint32_t *useCount);

    void eraseNotExistUuid();

    int masterServiceThreadMutexInit();

    inline void masterServiceThreadCondInit() { pthread_cond_init(&cond, NULL); }

    int handleWorkerHeartbeat(std::shared_ptr<RedisOperator> &redisClient, uint8_t *pOutData, uint32_t outLen, int fd, std::string requestId);

    int cloudPhoneSchedulingRsp(shared_ptr <WorkContextMgT> &pWorkerContext, RemoteSchedulingRspT *pPhoneRsp, int fd, std::string requestId);

    int cloudPhoneSchedulingReq(std::shared_ptr<RedisOperator> &redisClient, uint8_t *pOutData, uint32_t outLen, int fd, std::string requestId);

    int firstReportWorkerStatus(WorkerStatusRspT &pWorkerCap,
        shared_ptr<WorkContextMgT> pWorkerDevice, int fd, std::string requestId);

    int reportWorkerStatusInsertMem(std::shared_ptr<RedisOperator> &redisClient, WorkerStatusRspT &pWorkerCap,
        shared_ptr<WorkContextMgT> pWorkerDevice, int fd);

    int handleWorkerUpdateStatus(std::shared_ptr<RedisOperator> &redisClient, uint8_t *pOutData, uint32_t outLen, int fd, std::string requestId);

    int selectWorker(std::shared_ptr<RedisOperator> &redisClient, RemoteSchedulingReqT *pPhoneReq,
                                    shared_ptr <WorkContextMgT> &pWorkerCapInfo, uint8_t protocolVersion, std::string requestId);

    bool isSupportSchedulingReq(WorkContextMgT workerContext, RemoteSchedulingReqT *pPhoneReq, uint8_t protocolVersion, std::string requestId);

    std::string getWorkUuidContent(string workUuid);

    std::string getCmdContent(int resourceType, int dataType, int flavorType, int frameRate);

public:
    std::shared_ptr<SocketServer> pSocketServer;

private:
    std::mutex mutexUuidMap;
    std::mutex workerInfoMapMutex;
    std::unordered_map<std::string, WorkContextMgT> workerInfoMap;

    pthread_t pthreadId;
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    pthread_t heartPthreadId;
    int threadRunning;
};

int masterServiceStartInit(std::string configFileName);

uint64_t getCurrentTime(void);

uint32_t getIdleCount(void);

};
#endif